// Project: RacingGame, File: DiffuseNormalMapping.fx
// Creation date: 01.11.2005 04:56
// Last modified: 28.09.2006 03:51
// Author: Benjamin Nitschke (abi@exdream.com) (c) 2005
// Note: For testing FX Composer from NVIDIA can be used

string description = "Normal mapping shaders";

// Shader techniques in this file, all shaders work with vs/ps 1.1, shaders not
// working with 1.1 have names with 20 at the end:
// Diffuse           : Full vertex ambient+diffuse+specular lighting
// Diffuse20         : Same for ps20, only required for 3DS max to show shader!
//
// Specular           : Full vertex ambient+diffuse+specular lighting
// Specular20         : Nicer effect for ps20, also required for 3DS max to show shader!
//
// DiffuseSpecular    : Same as specular, but adding the specular component to diffuse (per vertex)
// DiffuseSpecular20  : Nicer effect for ps20, also required for 3DS max to show shader!
//
// Default variables, supported by the engine (may not be used here)
// If you don't need any global variable, just comment it out, this way
// the game engine does not have to set it!
//old: float4x4 worldViewProj         : WorldViewProjection;
float4x4 viewProj         : ViewProjection;
float4x4 world            : World;
float4x4 viewInverse      : ViewInverse;

float3 lightDir : Direction
<
	string UIName = "Light Direction";
	string Object = "DirectionalLight";
	string Space = "World";
> = {-0.65f, 0.65f, -0.39f}; // Normalized by app. FxComposer still uses inverted stuff :(

// The ambient, diffuse and specular colors are pre-multiplied with the light color!
float4 ambientColor : Ambient
<
	string UIName = "Ambient Color";
	string Space = "material";
> = {0.1f, 0.1f, 0.1f, 1.0f};
//> = {0.25f, 0.25f, 0.25f, 1.0f};

float4 diffuseColor : Diffuse
<
	string UIName = "Diffuse Color";
	string Space = "material";
> = {1.0f, 1.0f, 1.0f, 1.0f};

float4 specularColor : Specular
<
	string UIName = "Specular Color";
	string Space = "material";
> = {1.0f, 1.0f, 1.0f, 1.0f};

float shininess : SpecularPower
<
	string UIName = "Specular Power";
	string UIWidget = "slider";
	float UIMin = 1.0;
	float UIMax = 128.0;
	float UIStep = 1.0;
> = 16.0;

float alphaFactor
<
	string UIName = "Alpha factor";
	string UIWidget = "slider";
	float UIMin = 0.0;
	float UIMax = 1.0;
	float UIStep = 0.01;
> = 0.66f;//0.30f;//25;//0.1;

float fresnelBias = 0.5f;//0.5f;
float fresnelPower = 1.5f;// 5.0f;//4.0;
float reflectionAmount = 1.0f;

// Texture and samplers
texture diffuseTexture : Diffuse
<
	string UIName = "Diffuse Texture";
	string ResourceName = "Earth.jpg";
>;
sampler diffuseTextureSampler = sampler_state
{
	Texture = <diffuseTexture>;
	AddressU  = Wrap;//Clamp;
	AddressV  = Wrap;//Clamp;
	AddressW  = Wrap;//Clamp;
	MinFilter = Anisotropic;
	MagFilter = Anisotropic;
	MipFilter = Linear;
};

texture normalTexture : Diffuse
<
	string UIName = "Normal Texture";
	string ResourceName = "earthNormal.jpg";
>;
sampler normalTextureSampler = sampler_state
{
	Texture = <normalTexture>;
	AddressU  = Wrap;//Clamp;
	AddressV  = Wrap;//Clamp;
	AddressW  = Wrap;//Clamp;
	MinFilter = Anisotropic;
	MagFilter = Anisotropic;
	MipFilter = Linear;
};

//----------------------------------------------------

// Vertex input structure (used for ALL techniques here!)
struct VertexInput
{
	float3 pos      : POSITION;
	float2 texCoord : TEXCOORD0;
	float3 normal   : NORMAL;
	float3 tangent	: TANGENT;
};

// vertex shader output structure
struct VertexOutput
{
	float4 pos          : POSITION;
	float2 diffTexCoord : TEXCOORD0;
	float2 normTexCoord : TEXCOORD1;
	float3 lightVec     : COLOR0;
};

//----------------------------------------------------

// Common functions
float4 TransformPosition(float3 pos)//float4 pos)
{
	return mul(mul(float4(pos.xyz, 1), world), viewProj);
} // TransformPosition(.)

float3 GetWorldPos(float3 pos)
{
	return mul(float4(pos, 1), world).xyz;
} // GetWorldPos(.)

float3 GetCameraPos()
{
	return viewInverse[3].xyz;
} // GetCameraPos()

float3 CalcNormalVector(float3 nor)
{
	return normalize(mul(nor, (float3x3)world));//worldInverseTranspose));
} // CalcNormalVector(.)

// Get light direction
float3 GetLightDir()
{
	return lightDir;
} // GetLightDir()
	
float3x3 ComputeTangentMatrix(float3 tangent, float3 normal)
{
	// Compute the 3x3 tranform from tangent space to object space
	float3x3 worldToTangentSpace;
	worldToTangentSpace[0] =
		//left handed: mul(cross(tangent, normal), world);
		mul(cross(normal, tangent), world);
	worldToTangentSpace[1] = mul(tangent, world);
	worldToTangentSpace[2] = mul(normal, world);
	return worldToTangentSpace;
} // ComputeTangentMatrix(..)

//----------------------------------------------------

// Vertex shader function
VertexOutput VS_Diffuse(VertexInput In)
{
	VertexOutput Out = (VertexOutput) 0; 
	Out.pos = TransformPosition(In.pos);
	// Duplicate texture coordinates for diffuse and normal maps
	Out.diffTexCoord = In.texCoord;
	Out.normTexCoord = In.texCoord;

	// Compute the 3x3 tranform from tangent space to object space
	float3x3 worldToTangentSpace =
		ComputeTangentMatrix(In.tangent, In.normal);

	// Transform light vector and pass it as a color (clamped from 0 to 1)
	Out.lightVec = 0.5 + 0.5 *
		normalize(mul(worldToTangentSpace, GetLightDir()));

	// And pass everything to the pixel shader
	return Out;
} // VS_Diffuse(.)

// Pixel shader function, only used to ps2.0 because of .agb
float4 PS_Diffuse(VertexOutput In) : COLOR
{
	// Grab texture data
	float4 diffuseTexture = tex2D(diffuseTextureSampler, In.diffTexCoord);
	//return diffuseTexture;
	float3 normalTexture = tex2D(normalTextureSampler, In.normTexCoord).agb;
	float3 normalVector =
		(2.0 * normalTexture) - 1.0;
	// Normalize normal to fix blocky errors
	normalVector = normalize(normalVector);

	// Unpack the light vector to -1 - 1
	float3 lightVector =
		(2.0 * In.lightVec) - 1.0;

	// Compute the angle to the light
	float bump = saturate(dot(normalVector, lightVector));
	
	float4 ambDiffColor = ambientColor + bump * diffuseColor;
	return diffuseTexture * ambDiffColor;
} // PS_Diffuse(.)

// Techniques
technique Diffuse
{
	pass P0
	{
		VertexShader = compile vs_1_1 VS_Diffuse();
		sampler[0] = (diffuseTextureSampler);
		sampler[1] = (normalTextureSampler);
		PixelShaderConstant1[0] = <ambientColor>;
		PixelShaderConstant1[1] = <diffuseColor>;
		PixelShader = asm
		{
			// Optimized for ps_1_1, uses all possible 8 instructions.
			ps_1_1
			// Helper to calculate fake specular power.
			def c2, 1, 0, 0, 1
			// Sample diffuse and normal map
			tex t0
			tex t1
			// v0 is lightVector
			// Convert agb to xyz (costs 1 instuction)
			lrp r1, c2, t1.w, t1
			// Now work with r1 instead of t1
			dp3_sat r0, r1_bx2, v0_bx2
			mad r0, r0, c1, c0
			mul r0, r0, t0
		};
	} // pass P0
} // Diffuse

// Same for ps20 to show up in 3DS Max.
technique Diffuse20
{
	//pass P0
	//{
	//	VertexShader = compile vs_1_1 VS_Diffuse();
	//	PixelShader  = compile ps_2_0 PS_Diffuse();
	//} // pass P0
	pass P0
	{
		VertexShader = compile vs_1_1 VS_Diffuse();
		sampler[0] = (diffuseTextureSampler);
		sampler[1] = (normalTextureSampler);
		PixelShaderConstant1[0] = <ambientColor>;
		PixelShaderConstant1[1] = <diffuseColor>;
		PixelShader = asm
		{
			// Optimized for ps_1_1, uses all possible 8 instructions.
			ps_1_1
			// Helper to calculate fake specular power.
			def c2, 1, 0, 0, 1
			// Sample diffuse and normal map
			tex t0
			tex t1
			// v0 is lightVector
			// Convert agb to xyz (costs 1 instuction)
			lrp r1, c2, t1.w, t1
			// Now work with r1 instead of t1
			dp3_sat r0, r1_bx2, v0_bx2
			mad r0, r0, c1, c0
			mul r0, r0, t0
		};
	} // pass P0
} // Diffuse20

